﻿using T0yK4T.IO;
using System.Net;

namespace ToyChat.Client
{
    /// <summary>
    /// This is the default implementation of <see cref="IAuthenticationModule"/>
    /// </summary>
    public class AuthenticationModule : IAuthenticationModule
    {
        /// <summary>
        /// Attempts to authenticate on the server using the specified <see cref="IConnectionSettings"/>
        /// </summary>
        /// <param name="connection">The <see cref="CryptoConnection"/> that should be used to send and receive packets</param>
        /// <param name="settings">The <see cref="IConnectionSettings"/> to use for authentication purposes</param>
        /// <param name="client">The client that was used to initiate the connection and expects an <see cref="IClient.OnAuthenticated"/> call</param>
        /// <returns>True if succesful, otherwise false</returns>
        public bool Authenticate(CryptoConnection connection, IConnectionSettings settings, IClient client)
        {
            int s;
            Packet pkt = connection.ReadPacket(5000, out s);
            if (pkt.TypeID == (uint)InternalPacketTypes.LoginChallenge)
            {
                LoginChallenge challenge = ToySerializer.Deserialize<LoginChallenge>(pkt.Data);
                if (challenge != null)
                {
                    if (settings.Register)
                        pkt = this.Register(challenge, settings, connection);
                    else
                        pkt = this.Authenticate(challenge, settings, connection);

                    if (pkt.TypeID == (uint)InternalPacketTypes.TypeAliases)
                    {
                        client.OnAuthenticated(pkt.DeserializeData<AliasSpecifier>());
                        return true;
                    }
                }
            }
            return false;
        }

        /// <summary>
        /// Attempts to authenticate with the server connected on the specified connection using the specified settings
        /// </summary>
        /// <param name="challenge">The login challenge that was presented by the server</param>
        /// <param name="settings">The <see cref="IConnectionSettings"/> supplied by the user</param>
        /// <param name="connection">The <see cref="CryptoConnection"/> that the client is connected on</param>
        /// <returns>The response from the server to the registration request</returns>
        private Packet Authenticate(LoginChallenge challenge, IConnectionSettings settings, CryptoConnection connection)
        {
            challenge.Nickname = settings.Nickname;
            challenge.Username = settings.Username;
            if (challenge.PasswordRequired)
                challenge.Password = settings.Password;
            Packet pkt = new Packet((uint)InternalPacketTypes.LoginChallenge);
            pkt.SerializeData(challenge);
            connection.SendPacket(pkt);
            int s;
            pkt = connection.ReadPacket(5000, out s);
            return pkt;
        }

        /// <summary>
        /// Used to send a userregistration packet to the server
        /// </summary>
        /// <param name="challenge">The login challenge that was presented by the server</param>
        /// <param name="settings">The <see cref="IConnectionSettings"/> supplied by the user</param>
        /// <param name="connection">The <see cref="CryptoConnection"/> that the client is connected on</param>
        /// <returns>The response from the server to the registration request</returns>
        private Packet Register(LoginChallenge challenge, IConnectionSettings settings, CryptoConnection connection)
        {
            UserRegistrationPacket ureg = new UserRegistrationPacket { DesiredUsername = settings.Username, Email = settings.Email, DesiredPassword = settings.Password, DesiredNick = settings.Nickname };
            Packet pkt = new Packet((uint)InternalPacketTypes.RegisterUser);
            pkt.SerializeData(ureg);
            connection.SendPacket(pkt);
            int s;
            pkt = connection.ReadPacket(5000, out s);
            return pkt;
        }
    }

}